home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 October: Mac OS SDK / Dev.CD Oct 97 SDK1.toast / Development Kits (Disc 1) / QuickDraw GX / Programming Stuff / Sample Code / Printing Samples / Extensions… / Spooling ƒ / Spooling.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-06-15  |  23.3 KB  |  851 lines  |  [TEXT/MPS ]

  1. /*________________________________________________________
  2.  
  3.     File: Spooling.c
  4.     
  5.     This is the C source for a printing extension
  6.     that redirects spool files.
  7.     
  8.     Dave Hersey
  9.     Apple Developer Technical Support
  10.  
  11.      1/29/93 - dmh - Begat.
  12.      4/26/93 - dmh - Updated for b1.
  13.              - Switched to using the recommended
  14.                approach for initializing global data.
  15.      9/06/93 - dmh - Updated for b2.
  16.              - Switched to Exception.h assertion stuff
  17.                for error checking.
  18.     12/18/93 - dmh - Updated for b3.
  19.      3/22/94 - dmh - Updated for b4.
  20.      6/14/96 - cn  - Updated to support Universal Interfaces 2.1.
  21.  
  22.     (Note: all functions are in the Mark menu)
  23.     
  24. __________________________________________________________*/
  25.  
  26. #include "Spooling.h"
  27.  
  28. // globals:
  29.  
  30. short        gPrefsVRefNum;            // The volume it resides on.  (For FlushVol.)
  31. AliasHandle    gCurFolderAlias;        // The current folder alias used by the panel.
  32. Boolean        gSettingsChanged;        // True if we have panel changes to save.
  33. Boolean        gLastOnOffSetting;        // The "extTurnedOn" setting when we opened the panel.
  34.  
  35.  
  36. /*******************************************************************
  37.     InitGlobalData is used to initialize any global data we need to
  38.     in our initialize message override.  It's critical that you do
  39.     things this way, rather than access the data in the same scope
  40.     that you call NewMessageGlobals.  Otherwise, some compilers
  41.     will give you code that references an invalid A5 world.
  42.  
  43. ********************************************************************/
  44.  
  45. OSErr InitGlobalData()
  46. {
  47.     gCurFolderAlias = nil;
  48.     return noErr;
  49. }
  50.  
  51.  
  52. /*******************************************************************
  53.     SPInitialize is our override for GXInitialize.  We set up our
  54.     A5 world and jump off to initialize our global data.
  55.  
  56. ********************************************************************/
  57.  
  58. OSErr SPInitialize()
  59. {
  60.     OSErr    err;
  61.     
  62.     err = NewMessageGlobals(A5Size(), A5Init);
  63.     if (!err) err = InitGlobalData();
  64.  
  65.     return err;
  66. }
  67.  
  68.  
  69. /*******************************************************************
  70.     SPShutDown is our override for GXShutDown.  It just frees up
  71.     the A5 world we created in SPInitialize.
  72.  
  73. ********************************************************************/
  74.  
  75. OSErr SPShutDown()
  76. {
  77.     DisposeMessageGlobals();
  78.     return noErr;
  79. }
  80.  
  81.  
  82. /*******************************************************************
  83.     SPJobPrintDialog is our override for GXJobPrintDialog.  All we
  84.     do is set up our panel and then forward the message.
  85.  
  86. ********************************************************************/
  87.  
  88. OSErr SPJobPrintDialog(gxDialogResult *dlogResult)
  89. {
  90.     OSErr    err;
  91.     
  92.     err = SetUpPrintPanel();
  93.  
  94.     if (!err)
  95.         err = Forward_GXJobPrintDialog(dlogResult);
  96.     
  97.     return err;
  98. }
  99.  
  100.  
  101. /*******************************************************************
  102.     SetUpPrintPanel sets up our print panel, adding a default
  103.     SpoolCollection item to the job collection with our previous
  104.     settings.  This collection item has the values we'll use to
  105.     set up our panel's controls.
  106.  
  107. ********************************************************************/
  108.  
  109. OSErr SetUpPrintPanel()
  110. {
  111.     OSErr                    err;
  112.     gxPanelSetupRecord        panelSetupRec;
  113.     SpoolCollection            spoolConfig;
  114.  
  115. // Try to find our collection item.  We also try to get the default settings so
  116. // that we make sure the Prefs file is available.  If it's not, a new file will
  117. // be created by GetDefaultSettings.
  118.  
  119.     err = GetDefaultSettings(&spoolConfig);
  120.     nrequire(err, CanNotLoadDefaults);
  121.  
  122.  
  123. // Store our settings in the job collection.
  124.  
  125.     err = AddCollectionItem(GXGetJobCollection(GXGetJob()),
  126.                             kSpoolCollectionType,
  127.                             gxPrintingTagID,
  128.                             sizeof(SpoolCollection),
  129.                             &spoolConfig);
  130.  
  131.     nrequire(err, HaveCollectionMgrError);
  132.  
  133.  
  134. // Now, do the actual panel set up.
  135.  
  136.     gSettingsChanged = false;
  137.     gLastOnOffSetting = spoolConfig.extTurnedOn;
  138.  
  139.     panelSetupRec.panelResId        = r_spoolPanel;            // which panel resource?
  140.     panelSetupRec.resourceRefNum    = GXGetMessageHandlerResFile();    // where is it?
  141.     panelSetupRec.refCon            = 0;                    // we don't use this.
  142.     panelSetupRec.panelKind            = gxExtensionPanel;        // This is an extension panel.
  143.     
  144.     err = GXSetupDialogPanel(&panelSetupRec);
  145.  
  146. HaveCollectionMgrError:
  147. CanNotLoadDefaults:
  148.  
  149.     return err;
  150. }
  151.  
  152.  
  153. /*******************************************************************
  154.     SPHandlePanelEvent is our override for GXHandlePanelEvent.  If
  155.     the event is one of ours, we handle it.  Otherwise, we just
  156.     forward it down the chain.
  157.  
  158. ********************************************************************/
  159.  
  160. OSErr SPHandlePanelEvent(gxPanelInfoRecord *panelInfo)
  161. {
  162.     OSErr                        err = noErr;
  163.     GrafPtr                        oldPort;
  164.     DialogPtr                    pDlg;
  165.     Handle                        hItem;
  166.     Rect                        itemRect;
  167.     short                        itemType;
  168.     short                        oldResFile;
  169.     SpoolCollection                spoolConfig;
  170.     FSSpec                        curFSpec;
  171.  
  172.     pDlg = panelInfo->pDlg;
  173.     GetPort(&oldPort);
  174.     SetPort(pDlg);
  175.     
  176.     switch (panelInfo->panelEvt)
  177.     {
  178.  
  179. // If our panel is opening, go do any initialization we need to.
  180.  
  181.         case gxPanelOpenEvt:
  182.             OpenSpoolPanel(pDlg, panelInfo->itemCount);
  183.             break;
  184.  
  185. // If the user hit the "Print" or "Save" buttons, go save any
  186. // changes we need to.
  187.  
  188.         case gxPanelConfirmEvt:
  189.             err = SavePanelChanges();
  190.             break;
  191.  
  192.  
  193. // If the user hits the "Spool to…" button, prompt them for a folder.
  194. // If they select one, get our old collection item, move the FSSpec info to it,
  195. // and replace the old collection item with our modified one.
  196.  
  197.         case gxPanelHitEvt:
  198.             if (panelInfo->itemHit == (panelInfo->itemCount + d_selectDirectory))
  199.             {
  200.                 oldResFile = CurResFile();
  201.                 UseResFile(GXGetMessageHandlerResFile());
  202.  
  203.                 require(GetFolder(&curFSpec), GetFolder_Failed);
  204.                 
  205.                 gSettingsChanged = true;
  206.                 err = GetSpoolCollection(&spoolConfig);
  207.                 nrequire(err, GetSpoolCollection_Failed);
  208.  
  209.                 if (gCurFolderAlias != nil)
  210.                 {
  211.                     DisposHandle((Handle) gCurFolderAlias);
  212.                     gCurFolderAlias = nil;
  213.                 }
  214.                 
  215.                 err = NewAlias(nil, &curFSpec, &gCurFolderAlias);
  216.                 nrequire(err, NewAlias_Failed);
  217.                 AliasToPathName(gCurFolderAlias, (char *) spoolConfig.folderName, (char *) spoolConfig.volumeName);
  218.                 err = AddCollectionItem(GXGetJobCollection(GXGetJob()),
  219.                                         kSpoolCollectionType,
  220.                                         gxPrintingTagID,
  221.                                         sizeof(SpoolCollection),
  222.                                         &spoolConfig);
  223.  
  224.                 nrequire(err, AddCollectionItem_Failed);
  225.  
  226.                 GetDItem(panelInfo->pDlg, panelInfo->itemCount +d_folderName, &itemType, &hItem, &itemRect);
  227.                 SetIText(hItem, spoolConfig.folderName);
  228.                 GetDItem(panelInfo->pDlg, panelInfo->itemCount +d_volumeName, &itemType, &hItem, &itemRect);
  229.                 SetIText(hItem, spoolConfig.volumeName);
  230.  
  231.  
  232. AddCollectionItem_Failed:
  233. NewAlias_Failed:
  234. GetSpoolCollection_Failed:
  235. GetFolder_Failed:
  236.  
  237.                 UseResFile(oldResFile);
  238.             }
  239.             break;
  240.     }
  241.     
  242.     SetPort(oldPort);
  243.     return err;
  244. }
  245.  
  246.  
  247. /*******************************************************************
  248.     OpenSpoolPanel handles non-'xdtl' item initialization when we
  249.     open our panel.  Note that our items will be offset from
  250.     itemCount.  (So, if we want item #5 in our DITL, we pass
  251.     itemCount +5 to GetDItem.)
  252.  
  253. ********************************************************************/
  254.  
  255. void OpenSpoolPanel(DialogPtr pDlg, short itemCount)
  256. {
  257.     SpoolCollection        spoolConfig;
  258.     Handle                hItem;
  259.     Rect                itemRect;
  260.     short                itemType;
  261.  
  262. // Initialize the current file name displayed, based on the
  263. // settings in our SpoolCollection item.
  264.  
  265.     GetSpoolCollection(&spoolConfig);
  266.     GetDItem(pDlg, itemCount +d_folderName, &itemType, &hItem, &itemRect);
  267.     SetIText(hItem, spoolConfig.folderName);
  268.     GetDItem(pDlg, itemCount +d_volumeName, &itemType, &hItem, &itemRect);
  269.     SetIText(hItem, spoolConfig.volumeName);
  270. }
  271.  
  272.  
  273. /*******************************************************************
  274.     SavePanelChanges saves our panel settings to disk when they
  275.     change and the user confirms our panel.  We only save the
  276.     settings if they've changed since the last save.
  277.  
  278. ********************************************************************/
  279.  
  280. OSErr SavePanelChanges()
  281. {
  282.     OSErr                err;
  283.     SpoolCollection        spoolConfig, **spoolCollHdl;
  284.     short                oldResFile, resRefNum;
  285.  
  286.     err = GetSpoolCollection(&spoolConfig);
  287.     nrequire(err, GetSpoolCollection_Failed);
  288.  
  289.  
  290. // If our settings haven't changed, exit.
  291.  
  292.     gSettingsChanged = (gSettingsChanged || (spoolConfig.extTurnedOn != gLastOnOffSetting));
  293.     require(gSettingsChanged, SettingsHaveNotChanged);
  294.  
  295.  
  296. // Open the preferences file and set it as our current resource file.
  297.  
  298.     oldResFile = CurResFile();
  299.     err = OpenPrefsFile(&resRefNum, fsRdWrPerm);
  300.     nrequire(err, OpenPrefsFile_Failed);
  301.     UseResFile(resRefNum);
  302.  
  303.  
  304. // Create a handle to store our settings in, move the settings to that handle,
  305. // and then store it to disk as a resource. Also store the folder alias as a
  306. // resource.  If we can do this without any errors occurring, update our
  307. // global flags.
  308.  
  309.     spoolCollHdl = (SpoolCollection**) NewHandle(sizeof(SpoolCollection));
  310.     nrequire((err = MemError()), NewHandle_Failed);
  311.  
  312.     BlockMove(&spoolConfig, *spoolCollHdl, sizeof(SpoolCollection));
  313.     err = ReplaceResource((Handle) spoolCollHdl, kConfigType, kConfigID);
  314.     DisposHandle((Handle) spoolCollHdl);
  315.     nrequire(err, ReplaceResource_Failed);
  316.  
  317.     err = ReplaceResource((Handle) gCurFolderAlias, rAliasType, kAliasID);
  318.     nrequire(err, ReplaceResource_Failed);
  319.     gSettingsChanged = false;
  320.     gLastOnOffSetting = spoolConfig.extTurnedOn;
  321.  
  322.  
  323. ReplaceResource_Failed:
  324. NewHandle_Failed:
  325.  
  326.     UseResFile(oldResFile);
  327.     CloseResFile(resRefNum);
  328.  
  329.  
  330. OpenPrefsFile_Failed:
  331. SettingsHaveNotChanged:
  332. GetSpoolCollection_Failed:
  333.  
  334.     return err;
  335. }
  336.  
  337.  
  338. /*******************************************************************
  339.     SPCreateSpoolFile is our override for GXCreateSpoolFile.  It
  340.     just gets our collection item, sees if we're supposed to do
  341.     anything, and if so, changes where we create the spool file
  342.     before forwarding the message.
  343.     
  344.     If we get an error, we ignore it and try to create the spool
  345.     file without redirection.  This is so we're more user-friendly
  346.     in the case of a glitch.  At least their file will be created.
  347.  
  348. ********************************************************************/
  349.  
  350. OSErr SPCreateSpoolFile(FSSpec * anFSSpec, long createOptions, gxSpoolFile *spFile)    
  351. {
  352.     OSErr                    err;
  353.     SpoolCollection            **spoolCollHdl;
  354.     short                    oldResFile, resRefNum;
  355.     AliasHandle                folderAlias;
  356.     FSSpec                    theFSSpec;
  357.     Boolean                    wasChanged;
  358.  
  359. // Get and open our preferences file's resoruce fork.
  360.  
  361.     oldResFile = CurResFile();
  362.     err = OpenPrefsFile(&resRefNum, fsRdWrPerm);
  363.     nrequire(err, OpenPrefsFile_Failed);
  364.     UseResFile(resRefNum);
  365.  
  366.  
  367. // Load the folder alias and extension settings we last saved.
  368.  
  369.     folderAlias = (AliasHandle) Get1Resource(rAliasType, kAliasID);
  370.     nrequire((err = ResError()), CouldNotLoadAlias);
  371.     DetachResource((Handle) folderAlias);
  372.  
  373.     spoolCollHdl = (SpoolCollection **) Get1Resource(kConfigType, kConfigID);
  374.     nrequire((err = ResError()), CouldNotLoadSettings);
  375.  
  376.  
  377. // If the user had usd turned on, try to resolve the alias.  If it gets updated,
  378. // save the new alias to disk.
  379.  
  380.     if ((*spoolCollHdl)->extTurnedOn)
  381.     {
  382.         err = ResolveAlias(nil, folderAlias, &theFSSpec, &wasChanged);
  383.         nrequire(err, ResolveAlias_Failed);
  384.  
  385.         if (wasChanged)
  386.             err = ReplaceResource((Handle) folderAlias, rAliasType, kAliasID);
  387.  
  388.         nrequire(err, ReplaceResource_Failed);
  389.         FSMakeFSSpec(theFSSpec.vRefNum, GetActualDirID(&theFSSpec), anFSSpec->name, &theFSSpec);
  390.         BlockMove(&theFSSpec, anFSSpec, sizeof(FSSpec));
  391.     }
  392.  
  393.  
  394. // Release the settings resource, dispose of the alias handle, reset our current
  395. // resource file, close the prefs file, and forward the message down the chain.
  396.  
  397. ReplaceResource_Failed:
  398. ResolveAlias_Failed:
  399.     
  400.     ReleaseResource((Handle) spoolCollHdl);
  401.  
  402.  
  403. CouldNotLoadSettings:
  404.     
  405.     DisposHandle((Handle) folderAlias);
  406.  
  407.  
  408. CouldNotLoadAlias:
  409.  
  410.     UseResFile(oldResFile);
  411.     CloseResFile(resRefNum);
  412.  
  413.  
  414. OpenPrefsFile_Failed:
  415.  
  416.     return Forward_GXCreateSpoolFile(anFSSpec, createOptions, spFile);
  417. }
  418.  
  419.  
  420. /*******************************************************************
  421.     GetFolder is a replacement for StandardGetFile which allows
  422.     us to select directories.
  423.  
  424. ********************************************************************/
  425.  
  426. Boolean GetFolder(FSSpec *fSpec)
  427. {
  428.     OSErr                err;
  429.     Point                where = {-1,-1};
  430.     StandardFileReply    sfReply;
  431.     short                foundVRefNum;
  432.     long                foundDirID;
  433.  
  434. // Have the user select a folder using the GetFile dialog.
  435.  
  436. //    CustomGetFile((FileFilterYDProcPtr) FilterAllFiles, -1, nil, &sfReply, r_fileDlogID,
  437.     CustomGetFile((FileFilterYDUPP) FilterAllFiles, -1, nil, &sfReply, r_fileDlogID,
  438.                   where, (DlgHookYDUPP) MyDlgHook, nil, nil, nil, &sfReply);
  439.  
  440.     require(sfReply.sfGood, UserCancelledDialog);
  441.  
  442.  
  443. // If the user selected a volume, use the boot volume's desktop folder instead.
  444.  
  445.     nrequire(sfReply.sfIsVolume, UserDidNotChooseVolume);
  446.  
  447.     err = FindFolder(kOnSystemDisk, kDesktopFolderType, kCreateFolder, &foundVRefNum, &foundDirID);
  448.     nrequire(err, FindFolder_Failed);
  449.  
  450.     err = FSMakeFSSpec(foundVRefNum, foundDirID, nil, fSpec);
  451.  
  452.  
  453. UserDidNotChooseVolume:
  454.     
  455.     err = FSMakeFSSpec(sfReply.sfFile.vRefNum, sfReply.sfFile.parID, nil, fSpec);
  456.  
  457.  
  458. // There appears to be a bug in the CustomGetFile stuff.  Sometimes the directory
  459. // returned is bogus.  It seems to only happen when the desktop folder should be
  460. // selected.  MPW demonstrates this same bug, so I don't think it's my code.
  461. //
  462. // Test: using a system with more than one volume attached, use MPW's "Set Directory"
  463. // dialog to highlight the desktop icon of a drive other than the boot drive.  Now
  464. // hit the "Select Current Directory" button.  All's well.  Go back and highlight a
  465. // folder alias that belongs to a DIFFERENT volume (or highlight nothing at all) at
  466. // the desktop folder level, and select that.  You get an error. (-120, "directory
  467. // not found").  That's what I was seeing here too.
  468. //
  469. // It appears that the standard file stuff is looking for the desktop folder of the
  470. // wrong volume.  As a work-around, I simply return the boot drive's desktop folder
  471. // if I get a "directory not found" error.
  472.  
  473.     if (err == dirNFErr)
  474.     {
  475.         err = FindFolder(kOnSystemDisk, kDesktopFolderType, kCreateFolder, &foundVRefNum, &foundDirID);
  476.         nrequire(err, FindFolder_Failed);
  477.  
  478.         err = FSMakeFSSpec(foundVRefNum, foundDirID, nil, fSpec);
  479.     }
  480.  
  481.  
  482. FindFolder_Failed:
  483.  
  484.     if (err) sfReply.sfGood = false;
  485.  
  486.  
  487. UserCancelledDialog:
  488.  
  489.     return sfReply.sfGood;
  490. }
  491.  
  492.  
  493. /*******************************************************************
  494.     MyDlgHook is a dialog hook routine for CustomGetFile.
  495.  
  496. ********************************************************************/
  497.  
  498. pascal short MyDlgHook(short item, DialogPtr theDlg, Ptr userData)
  499. {
  500.     StandardFileReply    *curReply;
  501.     OSType                refCon;
  502.  
  503. // Unless we're being called from the main SF dialog, we don't do
  504. // anything.
  505.  
  506.     refCon = GetWRefCon(theDlg);
  507.     require((refCon == sfMainDialogRefCon), NotMainSFDialog);
  508.  
  509.  
  510. // If the user has selected the volume icon, change to the
  511. // desktop folder.  If they've selected our "Select Current Folder"
  512. // button, "OK" the dialog.
  513.  
  514.     curReply = (StandardFileReply *) userData;
  515.  
  516.     switch (item)
  517.     {
  518.         case sfItemVolumeUser:
  519.             curReply->sfFile.name[0] = '\0';
  520.             curReply->sfFile.parID = 2;
  521.             curReply->sfIsFolder = false;
  522.             curReply->sfIsVolume = false;
  523.             curReply->sfFlags = 0;
  524.             item = sfHookChangeSelection;
  525.             break;
  526.         
  527.         case d_selectItem:
  528.             item = sfItemOpenButton;
  529.             break;
  530.     }
  531.  
  532.  
  533. NotMainSFDialog:
  534.  
  535.     return item;
  536. }
  537.  
  538.  
  539. /*******************************************************************
  540.     FilterAllFiles is our CustomGetFile filterProc that only lets
  541.     directories be added to the display list.
  542.  
  543. ********************************************************************/
  544.  
  545. pascal Boolean FilterAllFiles(CInfoPBPtr pb, Ptr myDataPtr)
  546. {
  547. #pragma unused(myDataPtr);
  548.  
  549.      return (Boolean) !(pb->hFileInfo.ioFlAttrib & (1 <<4));
  550. }
  551.  
  552.  
  553. /*******************************************************************
  554.     GetActualDirID returns that actual directory ID (not the
  555.     parent's) of the folder referenced in the FSSpec * passed.
  556.  
  557. ********************************************************************/
  558.  
  559. long GetActualDirID(FSSpec * fSpec)
  560. {
  561.     DirInfo    infoPB;
  562.  
  563. // Get the passed folder's directory ID.
  564.  
  565.     infoPB.ioNamePtr = fSpec->name;
  566.     infoPB.ioVRefNum = fSpec->vRefNum;
  567.     infoPB.ioDrDirID = fSpec->parID;
  568.     infoPB.ioFDirIndex = 0;
  569.     PBGetCatInfo((CInfoPBPtr) &infoPB,false);
  570.  
  571.     return infoPB.ioDrDirID;
  572. }
  573.  
  574.  
  575. /*******************************************************************
  576.     GetDefaultSettings returns our previously saved settings.
  577.  
  578. ********************************************************************/
  579.  
  580. OSErr GetDefaultSettings(SpoolCollection *spoolConfig)
  581. {
  582.     OSErr                err;
  583.     long                foundDirID;
  584.     short                foundVRefNum, oldResRef, resRefNum;
  585.     SpoolCollection        **spoolCollHdl = nil;
  586.     FInfo                theFInfo;
  587.     FSSpec                curFSpec;
  588.     Boolean                wasChanged, isNew = false;
  589.     Str255                prefsFileName;
  590.  
  591. // Try to find our prefs file in the Preferences folder.
  592.     
  593.     err = OpenPrefsFile(&resRefNum, fsRdWrPerm);
  594.     require((err == fnfErr), PrefsFileExists);
  595.  
  596. // No prefs file-- create and open one.
  597.  
  598.     GetPrefsName((char *) prefsFileName);
  599.     err = FindFolder(kOnSystemDisk,kPreferencesFolderType, kCreateFolder, &foundVRefNum, &foundDirID);
  600.     nrequire(err, FindFolder_Failed1);
  601.  
  602.     HDelete(foundVRefNum, foundDirID, prefsFileName);
  603.     HCreateResFile(foundVRefNum, foundDirID, prefsFileName);
  604.     nrequire((err = ResError()), HCreateResFile_Failed);
  605.     
  606.     HGetFInfo(foundVRefNum, foundDirID, prefsFileName, &theFInfo);
  607.     theFInfo.fdType = kPrefsFileType;
  608.     theFInfo.fdCreator = kPrefsFileCreator;
  609.     HSetFInfo(foundVRefNum, foundDirID, prefsFileName, &theFInfo);
  610.  
  611.     resRefNum = HOpenResFile(foundVRefNum, foundDirID, prefsFileName, fsRdWrPerm); 
  612.     nrequire((err = ResError()), HOpenResFile_Failed);
  613.  
  614.  
  615. PrefsFileExists:
  616.  
  617.     oldResRef = CurResFile();
  618.     UseResFile(resRefNum);
  619.  
  620.  
  621. // Get a previously saved or newly created alias to the spool folder.
  622.  
  623.     if (gCurFolderAlias != nil)
  624.         DisposHandle((Handle) gCurFolderAlias);
  625.  
  626.     gCurFolderAlias = (AliasHandle) Get1Resource(rAliasType, kAliasID);
  627.  
  628.     if (gCurFolderAlias != nil)
  629.         DetachResource((Handle) gCurFolderAlias);
  630.     else                                        // no resource-- create one.
  631.     {
  632.         err = FindFolder(kOnSystemDisk, kPrintMonitorDocsFolderType, kCreateFolder, &foundVRefNum, &foundDirID);
  633.         nrequire(err, FindFolder_Failed2);
  634.  
  635.         FSMakeFSSpec(foundVRefNum, foundDirID, nil, &curFSpec);
  636.         err = NewAlias(nil, &curFSpec, &gCurFolderAlias);
  637.         nrequire(err, NewAlias_Failed);
  638.  
  639.         err = ReplaceResource((Handle) gCurFolderAlias, rAliasType, kAliasID);
  640.         nrequire(err, ReplaceResource_Failed);
  641.     }
  642.  
  643.  
  644. // Get a previously saved or newly created handle to our panel settings.
  645.  
  646.     spoolCollHdl = (SpoolCollection **) Get1Resource(kConfigType, kConfigID);
  647.  
  648.     if (spoolCollHdl != nil)
  649.         DetachResource((Handle) spoolCollHdl);
  650.     else                                        // no resource-- create one.
  651.     {
  652.         spoolCollHdl = (SpoolCollection **) NewHandle(sizeof(SpoolCollection));
  653.         (*spoolCollHdl)->extTurnedOn = kRedirectDisabled;
  654.         isNew = true;
  655.     }
  656.  
  657.  
  658. // If our alias needs to be updated, update it and save off the new volume
  659. // and folder names.  In any case, move our settings into the passed pointer
  660. // and return them to the caller.
  661.  
  662.     require_action((spoolCollHdl != nil), CanNotCreateSettings, err = MemError(););
  663.  
  664.     HLock((Handle) spoolCollHdl);
  665.     wasChanged = AliasToPathName(gCurFolderAlias, (char *) (*spoolCollHdl)->folderName, (char *) (*spoolCollHdl)->volumeName);
  666.     HUnlock((Handle) spoolCollHdl);
  667.  
  668.     if (wasChanged || isNew)
  669.         err = ReplaceResource((Handle) spoolCollHdl, kConfigType, kConfigID);
  670.  
  671.     BlockMove(*spoolCollHdl, spoolConfig, sizeof(SpoolCollection));
  672.     DisposHandle((Handle) spoolCollHdl);
  673.  
  674.  
  675. CanNotCreateSettings:
  676. ReplaceResource_Failed:
  677. NewAlias_Failed:
  678. FindFolder_Failed2:
  679.  
  680.     UseResFile(oldResRef);
  681.     CloseResFile(resRefNum);
  682.  
  683.  
  684. HOpenResFile_Failed:
  685. HCreateResFile_Failed:
  686. FindFolder_Failed1:
  687.     
  688.     return err;
  689. }
  690.  
  691.  
  692. /*******************************************************************
  693.     OpenPrefsFile opens our preferences file.
  694.  
  695. ********************************************************************/
  696.  
  697. OSErr OpenPrefsFile(short *resRefNum, char permission)
  698. {
  699.     OSErr    err;
  700.     long    foundDirID;
  701.     short    foundVRefNum;
  702.     Boolean    targetIsFolder, wasAliased;
  703.     FSSpec    fSpec;
  704.     Str255    prefsFileName;
  705.  
  706.  
  707. // Find the preferences folder and open our preferences file, if it exists.
  708.  
  709.     err = FindFolder(kOnSystemDisk, kPreferencesFolderType, kCreateFolder, &foundVRefNum, &foundDirID);
  710.  
  711.     if (!err)
  712.     {
  713.         GetPrefsName((char *) prefsFileName);
  714.         err = FSMakeFSSpec(foundVRefNum, foundDirID, prefsFileName, &fSpec);
  715.         nrequire(err, ThereWasAnError);
  716.  
  717.         err = ResolveAliasFile(&fSpec, true, &targetIsFolder, &wasAliased);
  718.         nrequire(err, ThereWasAnError);
  719.  
  720.         gPrefsVRefNum = fSpec.vRefNum;
  721.         *resRefNum = FSpOpenResFile(&fSpec, permission);
  722.         err = ResError();
  723.     }
  724.  
  725.  
  726. ThereWasAnError:
  727.  
  728.     return err;
  729. }
  730.  
  731.  
  732. /*******************************************************************
  733.     ReplaceResource adds or replaces a resource in the current
  734.     resource file.  (Make sure the file you want to access is
  735.     open and is the current resource file!)  The resource is
  736.     detached when the data is returned, so call DisposHandle on
  737.     it, NOT ReleaseResource.
  738.  
  739. ********************************************************************/
  740.  
  741. OSErr ReplaceResource(Handle newData, OSType resourceType, short resourceID)
  742. {
  743.     OSErr    err;
  744.     Handle    oldResource;
  745.  
  746.     oldResource = Get1Resource(resourceType, resourceID);
  747.     
  748.     require((oldResource != nil), AddingNewResource);
  749.     RmveResource(oldResource);
  750.     DisposHandle(oldResource);
  751.  
  752.  
  753. AddingNewResource:
  754.     
  755.     AddResource(newData, resourceType, resourceID, "\p");
  756.     
  757.     if (!(err = ResError()))
  758.         WriteResource(newData);
  759.  
  760.     UpdateResFile(CurResFile());
  761.     FlushVol(nil, gPrefsVRefNum);
  762.  
  763.     if (!err) DetachResource(newData);
  764.     return err;
  765. }
  766.  
  767.  
  768. /*******************************************************************
  769.     GetSpoolCollection is a handy routine that retrieves our
  770.     SpoolCollection item from the current job collection.
  771.  
  772. ********************************************************************/
  773.  
  774. OSErr GetSpoolCollection(SpoolCollection *spoolCollect)
  775. {
  776.     OSErr        err;
  777.     Collection    jobCollection;
  778.  
  779.     jobCollection = GXGetJobCollection(GXGetJob());
  780.  
  781.     err = GetCollectionItem(jobCollection,
  782.                             kSpoolCollectionType,
  783.                             gxPrintingTagID,
  784.                             nil,
  785.                             spoolCollect);
  786.     return err;
  787. }
  788.  
  789.  
  790. /*******************************************************************
  791.     AliasToPathName extracts the folder and volume name from the
  792.     passed alias, updating them if it needs to.  (I use MatchAlias
  793.     so that I can avoid the user dialogs and volume mounting if
  794.     the alias spans a network.)  If these types of aliases are
  795.     out of date, they will be updated at CreateSpoolFile time.
  796.     We return true if the alias has been updated and needs to be
  797.     saved off.
  798.  
  799. ********************************************************************/
  800.  
  801. Boolean AliasToPathName(AliasHandle anAlias, char *folderName, char *volName)
  802. {
  803.     Boolean        wasChanged;
  804.     OSErr        err;
  805.     FSSpec        fSpec;
  806.     short        num = 1;
  807.  
  808. // See if we can find the folder referenced by the alias.
  809.  
  810.     err = MatchAlias(nil, kARMSearch, anAlias, &num, (FSSpecArrayPtr) &fSpec,
  811.                      &wasChanged, nil, nil);
  812.  
  813.     nrequire_action(err, AliasMgrError, wasChanged = false;);
  814.  
  815.  
  816. // If we found the folder but its path has changed, update the folder and
  817. // volume names referenced.
  818.  
  819.     if (wasChanged)
  820.         err = UpdateAlias(nil, &fSpec, anAlias, &wasChanged);
  821.  
  822.     nrequire(err, AliasMgrError);
  823.     GetAliasInfo(anAlias, asiAliasName, (unsigned char *) folderName);
  824.     GetAliasInfo(anAlias, asiVolumeName, (unsigned char *) volName);
  825.  
  826.  
  827. AliasMgrError:
  828.  
  829.     return wasChanged;
  830. }
  831.  
  832.  
  833. /*******************************************************************
  834.     GetPrefsName returns the name of our preferences file.
  835.  
  836. ********************************************************************/
  837.  
  838. void GetPrefsName(char *prefsName)
  839. {
  840.     short    oldResFile;
  841.  
  842. // We just load the name of our preferences file from our extension.
  843.  
  844.     oldResFile = CurResFile();
  845.     UseResFile(GXGetMessageHandlerResFile());
  846.  
  847.     GetIndString((unsigned char *) prefsName, r_stringRsrc, r_prefsStrIdx);
  848.     UseResFile(oldResFile);
  849. }
  850.  
  851.